﻿using System;

using Microsoft.Xna.Framework;
using System.Collections.Generic;

namespace Spacer {
	public class GalaxyGenerator : Generator {
		public GalaxyGenerator( Galaxy galaxy ) : base() {
			Galaxy = galaxy;
			Galaxy.Seed = Seed;

			QuadTree = new Quadtree<Tree<SolarSys>>(
				-150, -150,
				300, 300,
				10
			);
			Nodes = new List<Tree<SolarSys>>();

            SeedOffset = 0;

            //Create Abundant minerals in galaxy
            AbundantMineral = Random.Next(5);

			//MaxRange = (float)Random.NextDouble() * 5 + 15;
		}

		public GalaxyGenerator( Galaxy galaxy, int seed ) : base(seed) {
			Galaxy = galaxy;
			Galaxy.Seed = Seed;

			QuadTree = new Quadtree<Tree<SolarSys>>(
				-150, -150,
				300, 300,
				10
			);
			Nodes = new List<Tree<SolarSys>>();

            SeedOffset = 0;

            //Create Abundant minerals in galaxy
            AbundantMineral = Random.Next(5);
			//MaxRange = (float)Random.NextDouble() * 5 + 15;
		}

		// ----------

		public override void Generate() {
			// Create root
			AddSolarSystem( Vector2.Zero, Galaxy.SystemsGraph );

			// Copy values over to the Galaxy
			//Galaxy.Systems = Nodes;

			// Create root's children
			int count = Random.Next(TREEROOT_CHILDREN_MIN, TREEROOT_CHILDREN_MAX);
			for (int i = 0; i < count; i++)
			{
				if (Random.NextDouble() >= TREENODE_CHILDREN_CHANCE)
					continue;

				// Calculate distance, direction, and final position
				float dist = (float)Random.NextDouble() * DISTANCE_BETWEEN_TOMAX + DISTANCE_BETWEEN_MIN; // Parsecs
				double dir = ((double)i / (double)count) * MathHelper.TwoPi;
				Vector2 dir_vec = new Vector2(
					(float)Math.Cos( dir ),
					(float)Math.Sin( dir )
				);
				Vector2 pos = /*root.Position + */dist * dir_vec;
				
				// Create new system and node if it is out of range of another
				if( QuadTree.FindObjects(pos.X,pos.Y,DISTANCE_BETWEEN_CHECK).Count == 0 ) {
					Tree<SolarSys> child = new Tree<SolarSys>();
					Galaxy.SystemsGraph.AddChildNode( child );
					AddSolarSystem( pos, child );
				}
			}

			// Recursively create children
			int depth = Random.Next(TREENODE_DEPTH_MIN, TREENODE_DEPTH_MAX);
			foreach (Tree<SolarSys> node in Galaxy.SystemsGraph)
			{
				GenerateChildren(
					node,
					Math.Atan2(
						node.Data.Position.Y,
						node.Data.Position.X
					),
					depth
				);
			}

			// Loop through all nodes and link to eachother
			foreach(Tree<SolarSys> node in Nodes) {
				node.ClearChildren();
				List<Quadtree<Tree<SolarSys>>.QuadtreeObject> neighbors = QuadTree.FindObjects( node.Data.Position.X, node.Data.Position.Y, DISTANCE_CONNECTION_CHECK );
				foreach(Quadtree<Tree<SolarSys>>.QuadtreeObject obj in neighbors) {
					if( obj.Object != node )
						node.AddChildNode( obj.Object );
				}
			}
		}

		private void GenerateChildren( Tree<SolarSys> node, double dir, int depth )
		{
			if (depth == 0)
				return;

			int children = Random.Next(TREENODE_CHILDREN_MIN, TREENODE_CHILDREN_MAX);
			double offset = Random.NextDouble() * MathHelper.PiOver2 + MathHelper.PiOver2;

			// Generate children
			for (int i = 0; i < children; i++)
			{
				// Calculate distance, direction, and final position
				float dist = (float)Random.NextDouble() * DISTANCE_BETWEEN_TOMAX + DISTANCE_BETWEEN_MIN;
				double final_dir = dir + (Random.NextDouble() * (offset * 2) - offset);
				Vector2 dir_vec = new Vector2(
					(float)Math.Cos( final_dir ),
					(float)Math.Sin( final_dir )
				);
				Vector2 pos = node.Data.Position + dist * dir_vec;

				// Create new system and node if it is out of range of another
				if( QuadTree.FindObjects(pos.X,pos.Y,DISTANCE_BETWEEN_CHECK).Count == 0 ) {
					Tree<SolarSys> child = new Tree<SolarSys>();
					node.AddChildNode( child );
					AddSolarSystem( pos, child );
				}
			}

			// Recursively create children
			foreach (Tree<SolarSys> child in node)
			{
				GenerateChildren(
					child,
					Math.Atan2(
						child.Data.Position.Y - node.Data.Position.Y,
						child.Data.Position.X - node.Data.Position.X
					),
					depth - 1
				);
			}
		}

		private void AddSolarSystem( Vector2 pos, Tree<SolarSys> node ) {
			// New solar system!!!
			SolarSys sys = new SolarSys( Galaxy, typeof(SolarSysGenerator) );
			node.Data = sys;
            sys.Position = pos;			    // Position
			sys.GalaxyNode = node;		    // Pass node
            sys.Seed = Seed + SeedOffset;   // Pass seed
			QuadTree.Add( node, pos );	    // Add to graph

			Nodes.Add( node );				// Add to list of nodes
			Galaxy.Systems.Add( sys.ID, sys );

			// Do extra stuff?!?!?!
			// ...
            sys.AbundantMineral = AbundantMineral;

            SeedOffset++;
		}

		// ----------

		public Galaxy Galaxy { get; private set; }
		public List<Tree<SolarSys>> Nodes { get; private set; }
		public Quadtree<Tree<SolarSys>> QuadTree { get; private set; }
		//public float MaxRange { get; private set; }
        public int AbundantMineral;
        
        private int SeedOffset;
		
		private const int TREEROOT_CHILDREN_MIN = 5;
		private const int TREEROOT_CHILDREN_MAX = 8;

		private const float DISTANCE_BETWEEN_MIN = 3f;
		private const float DISTANCE_BETWEEN_TOMAX = 2f; // MIN + TOMAX = MAX = 5f
		private const float DISTANCE_BETWEEN_CHECK = 2f;
		private const float DISTANCE_CONNECTION_CHECK = 5f;
		
		private const int TREENODE_CHILDREN_MIN = 2;
		private const int TREENODE_CHILDREN_MAX = 3;
		private const double TREENODE_CHILDREN_CHANCE = 0.75;

		private const int TREENODE_DEPTH_MIN = 6;
		private const int TREENODE_DEPTH_MAX = 14;
	}
}
